﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Text;

namespace FtpCrawler
{
    /// <summary>
    /// ftp爬行器主类
    /// </summary>
    public class MainCrawler
    {
        private List<FtpConfig> _ftpConfigs;
        private IFileProcessor _processor;
        private Queue<FtpFileInfo> _dirQueue = new Queue<FtpFileInfo>();  // 目录队列

        public MainCrawler(IFileProcessor processor)
        {
            this._processor = processor;
        }

        public MainCrawler(IFileProcessor processor, List<FtpConfig> ftpConfigs)
        {
            this._processor = processor;
            this._ftpConfigs = ftpConfigs;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ftpConfigs">需要爬行的FTP站点列表</param>
        public MainCrawler(List<FtpConfig> ftpConfigs)
        {

        }

        /// <summary>
        /// 构造函数，从XML配置文件中获取FTP站点列表
        /// </summary>
        /// <param name="xmlFile">指定XML文件</param>
        public MainCrawler(string xmlFile)
        {
            this._ftpConfigs = MainCrawler.LoadConfigsFromXml(xmlFile);
        }

        public IFileProcessor Processor
        {
            get { return _processor; }
            set { _processor = value; }
        }

        /// <summary>
        /// 从XML配置文件中获取FTP站点列表
        /// </summary>
        /// <param name="xmlFile">指定XML文件</param>
        /// <returns>FTP站点列表</returns>
        public static List<FtpConfig> LoadConfigsFromXml(string xmlFile)
        {
            // TODO: 暂未实现
            return null;
        }

        /// <summary>
        /// 从主机上获得文件列表
        /// </summary>
        /// <param name="ftpConfig">FTP主机配置信息</param>
        public void GetFileListsFromHost(FtpConfig ftpConfig)
        {
            // 根目录
            FtpFileInfo fileInfo = new FtpFileInfo();
            fileInfo.IsFile = false;
            fileInfo.Path = string.Format("ftp://{0}:{1}{2}", ftpConfig.Site, ftpConfig.Port, ftpConfig.Path);

            this._dirQueue.Enqueue(fileInfo);  // 根目录入队

            FtpClient ftpClient = new FtpClient(ftpConfig.Site + ":" + ftpConfig.Port,
                    new NetworkCredential(ftpConfig.Username, ftpConfig.Password));
            ftpClient.Encoding = ftpConfig.Encoding;

            this.GetFilesListFromDirs(ftpClient);
        }

        /// <summary>
        /// 从目录队列中获得文件列表
        /// </summary>
        /// <param name="ftpClient"></param>
        private void GetFilesListFromDirs(FtpClient ftpClient)
        {
            if (ftpClient == null)
            {
                return;
            }
            FtpFileInfo dirInfo = null;
            Uri uri = null;
            FtpWebRequest request = null;
            WebResponse response = null;
            StreamReader reader = null;
            string line = null;
            IFtpFileParser fileParser = null;
            FtpFileInfo fileInfo = null;

            // 开始处理
            this._processor.StartProcess();
            while (this._dirQueue.Count > 0)
            {
                dirInfo = this._dirQueue.Dequeue();

                string path = dirInfo.Path;
                if (!string.IsNullOrEmpty(dirInfo.Filename))
                {
                    path += (dirInfo.Filename + "/");
                }

                try
                {
                    uri = new Uri(path);
                    request = ftpClient.CreateFtpWebRequest(uri, WebRequestMethods.Ftp.ListDirectoryDetails);

                    response = request.GetResponse();
                    reader = new StreamReader(
                        response.GetResponseStream(), Encoding.GetEncoding(ftpClient.Encoding));
                    line = reader.ReadLine();

                    if (fileParser == null)
                    {
                        fileParser = FtpFileParserFactory.GetInstance().CreateFileParser(line);
                        if (fileParser == null)
                        {
                            // 创建文件解析器失败，请检查
                            this._processor.ProcessError("Error: " + uri.ToString() + " 创建文件解析器失败");
                        }
                    }
                    fileInfo = null;
                    while (line != null)
                    {
                        fileInfo = fileParser.ParseFile(line, uri.ToString());
                        if (!(fileInfo.Filename == "." || fileInfo.Filename == ".."))
                        {
                            if (!fileInfo.IsFile)
                            {
                                _dirQueue.Enqueue(fileInfo);
                            }
                            this._processor.ProcessFile(fileInfo);
                        }
                        line = reader.ReadLine();
                    }
                    reader.Close();
                    response.Close();
                }
                catch (UriFormatException uriFormatException)
                {
                    if (uri != null)
                    {
                        this._processor.ProcessError("Error : " + "ftp://" + ftpClient.Hostname + path + " 处理错误……\n" +
                            uriFormatException.ToString() + "\n" + uriFormatException.Message + "\n" + uriFormatException.StackTrace);
                    }
                }
                catch (Exception ex)
                {
                    // 网络访问出错，请检查！
                    if (fileInfo.Filename != null)
                    {
                        this._processor.ProcessError("Error : " + fileInfo.Path + fileInfo.Filename + " 处理错误……\n" +
                            ex.ToString() + "\n" + ex.Message + "\n" + ex.StackTrace);
                    }
                    else
                    {
                        this._processor.ProcessError("Error : 未知错误……\n" +
                            ex.ToString() + "\n" + ex.Message + "\n" + ex.StackTrace);
                    }
                }
            }
            this._processor.ProcessCompleted();

            return;
        }
    }
}
